﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.MefExtensions.Modularity;
using System.Diagnostics;
using System.Reflection;
using System.ComponentModel.Composition.ReflectionModel;
using Microsoft.Practices.Prism.Regions;
using System.Windows;

namespace Snowdreamist.Prism.Region
{
	/// <summary>
	/// 视图组合管理器
	/// 默认是按照需要进行初始化，因为初始化之后会相应组合容器的变更通知，如果实在添加ComposablePart的过程中初始化好，会重新扫面容器多次
	/// 因此推荐在组合容器完成最初的组合之后再调用一次，因此使用OnDemand初始化方式
	/// </summary>
	[ModuleExport("ViewComposeManager",typeof(ViewComposeManager),InitializationMode=InitializationMode.OnDemand)]
	public class ViewComposeManager : IModule
	{
		/// <summary>
		/// 目标视图键
		/// </summary>
		public const string TargetViewKey = "TargetViewName";
		/// <summary>
		/// 目标区域键
		/// </summary>
		public const string TargetRegionKey = "TargetRegionName";
		/// <summary>
		/// 顺序键
		/// </summary>
		public const string OrderKey = "Order";
		/// <summary>
		/// 协定类型键
		/// </summary>
		public const string ContractTypeKey = "ExportTypeIdentity";

		#region 私有域
		private static string IViewPartTypeName = typeof(IViewPart).FullName;
		private static string IViewPartFactoryTypeName = typeof(IViewPartFactory).FullName;
		/// <summary>
		/// 组合容器
		/// </summary>
		private CompositionContainer Container { get; set; }
		/// <summary>
		/// 组合ViewPart
		/// </summary>
		private void ComposeViewParts(IEnumerable<ViewPart> viewParts, IRegionManager regionManager)
		{
			var orderredViewParts = viewParts.OrderBy(v => v.Order);
			try
			{
				foreach (var x in orderredViewParts)
					regionManager.AddToRegion(x.TargetRegionName, x.Value);
			}
			catch (Exception exp)
			{
				if (Debugger.IsAttached)
					throw;
				else
					Debug.Print("ComposeViewParts failed. Exception: {0}{1}", Environment.NewLine, exp.ToString());
			}
		}
		/// <summary>
		/// 检查导出定义
		/// </summary>
		/// <param name="exportDefinition">导出定义</param>
		/// <param name="viewName">试图名称</param>
		/// <returns>是否符合要求</returns>
		private bool CheckViewExportDefinition(ExportDefinition exportDefinition, string viewName,string typeName)
		{
			if (!exportDefinition.Metadata.ContainsKey(TargetViewKey))
				return false;
			if ((string)exportDefinition.Metadata[ContractTypeKey] != typeName)
				return false;

			object targetViewValue = exportDefinition.Metadata[TargetViewKey];
			if (targetViewValue is string)
			{
				if (((string)targetViewValue) == viewName)
					return true;
				else
					return false;
			}
			else
				return false;
		}
		/// <summary>
		/// 组合ViewPartFactory
		/// </summary>
		/// <param name="viewPartFactoryExports"></param>
		/// <returns></returns>
		private IEnumerable<ViewPart> ComposeViewPartFactory(IEnumerable<Export> viewPartFactoryExports)
		{
			List<ViewPart> viewParts = new List<ViewPart>();
			foreach (var x in viewPartFactoryExports)
			{
				IViewPartFactory factory = x.Value as IViewPartFactory;
				if (factory != null)
					viewParts.AddRange(factory.Compose());
			}
			return viewParts;
		}
		#endregion

		#region AttachedBehavior
		public static bool GetIsAutoComposeView(DependencyObject dpObj)
		{
			if (dpObj == null)
				throw new ArgumentNullException("dpObj");
			return (bool)dpObj.GetValue(IsAutoComposeViewProperty);
		}
		public static void SetIsAutoComposeView(DependencyObject dpObj,bool value)
		{
			if (dpObj == null)
				throw new ArgumentNullException("dpObj");
			dpObj.SetValue(IsAutoComposeViewProperty, value);
		}
		/// <summary>
		/// 是否自动组合视图
		/// </summary>
		public static readonly DependencyProperty IsAutoComposeViewProperty =
			DependencyProperty.RegisterAttached("IsAutoComposeView", typeof(bool), typeof(ViewComposeManager), new PropertyMetadata(false, OnIsAutoComposeViewPropertyChanged));
		private static void OnIsAutoComposeViewPropertyChanged(DependencyObject dpObj,DependencyPropertyChangedEventArgs e)
		{
			if ((bool)e.NewValue)
			{
				//启动
				FrameworkElement element = dpObj as FrameworkElement;
				if(element != null)
					element.Loaded += new RoutedEventHandler(AttachedObject_Loaded);
			}
		}
		/// <summary>
		/// 附加的对象加载时，进行组合
		/// </summary>
		private static void AttachedObject_Loaded(object sender, RoutedEventArgs e)
		{
			FrameworkElement element = sender as FrameworkElement;
			if (element != null)
			{
				element.Loaded -= new RoutedEventHandler(AttachedObject_Loaded);
				//组合
				IView view = element as IView;
				if (view != null)
					view.OnComposing();
				
				var r = Current.Compose(element);
				
				if(view != null)
				{
					SetViewParts(element, r);
					view.OnComposed();
				}
			}
		}
		public static IEnumerable<ViewPart> GetViewParts(DependencyObject dpObj)
		{
			if (dpObj == null)
				throw new ArgumentNullException("dpObj");
			return dpObj.GetValue(ViewPartsProperty) as IEnumerable<ViewPart>;
		}
		public static void SetViewParts(DependencyObject dpObj,IEnumerable<ViewPart> value)
		{
			if (dpObj == null)
				throw new ArgumentNullException("dpObj");
			dpObj.SetValue(ViewPartsProperty,value);
		}
		/// <summary>
		/// 试图部件依赖属性
		/// </summary>
		public readonly static DependencyProperty ViewPartsProperty =
			DependencyProperty.RegisterAttached("ViewParts", typeof(IEnumerable<ViewPart>), typeof(ViewComposeManager), new PropertyMetadata(null));
		#endregion

		#region Current
		/// <summary>
		/// 当前ViewComposeManager
		/// </summary>
		public static ViewComposeManager Current { get; private set; }
		#endregion

		/// <summary>
		/// 创建一个ViewComposeManager
		/// </summary>
		/// <param name="container">组合容器</param>
		[ImportingConstructor]
		public ViewComposeManager(CompositionContainer container)
		{
			Current = this;
			Container = container;
		}

		#region 组合
		/// <summary>
		/// 组合
		/// </summary>
		/// <param name="dpObj">依赖对象</param>
		public IEnumerable<ViewPart> Compose(DependencyObject dpObj)
		{
			string viewName = RegionHelper.GetViewDeclareName(dpObj.GetType());
			if (viewName == null)
			{
				Debug.Print("ViewComposeManager.Compose failed. Target: {0} Reason: viewName == null", dpObj.ToString());
				return null;
			}
			IRegionManager regionManager = RegionManager.GetRegionManager(dpObj);
			if (regionManager == null)
			{
				try
				{
					regionManager = Container.GetExportedValue<IRegionManager>();
				}
				catch (Exception exp)
				{
					Debug.Print("ViewComposeManager.Compose failed. Target: {0} Exception: {1}{2}", dpObj.ToString(), Environment.NewLine, exp.ToString());
					return null;
				}
			}

			try
			{
				return Compose(viewName, regionManager);
			}
			catch (Exception exp)
			{
				if (Debugger.IsAttached)
					throw;
				else
				{
					Debug.Print("Compose Failed. Exception:{0}{1}", Environment.NewLine, exp);
					return null;
				}
			}
		}
		/// <summary>
		/// 组合视图
		/// </summary>
		/// <param name="viewName">要组合的视图名称</param>
		/// <param name="regionManager">该视图上的RegionManager</param>
		public IEnumerable<ViewPart> Compose(string viewName, IRegionManager regionManager)
		{
			//ViewPart
			ImportDefinition viewPartImportDefinition = new ImportDefinition(d => CheckViewExportDefinition(d, viewName, ViewComposeManager.IViewPartTypeName), null, ImportCardinality.ZeroOrMore, false, true);
			var viewPartExports = Container.GetExports(viewPartImportDefinition);
			var viewParts = viewPartExports.Select(e => RegionHelper.GetViewPart(e));
			//ViewPartFactory
			ImportDefinition viewPartFactoryImportDefinition = new ImportDefinition(d => CheckViewExportDefinition(d, viewName, ViewComposeManager.IViewPartFactoryTypeName), null, ImportCardinality.ZeroOrMore, false, true);
			var viewPartFactoryExports = Container.GetExports(viewPartFactoryImportDefinition);
			var viewPartFactoryViewParts = ComposeViewPartFactory(viewPartFactoryExports);
			//组合全部ViewParts
			viewParts.Concat(viewPartFactoryViewParts);
			ComposeViewParts(viewParts, regionManager);
			return viewParts.ToArray();
		}
		#endregion

		#region IModule 成员
		/// <summary>
		/// 初始化
		/// </summary>
		public void Initialize()
		{
		}
		#endregion
	}
}
